﻿using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using log4net;
using PowerGui.AddOn;
using PowerGui.AddOn.Interfaces.Properties;

namespace PowerGuiVsx.Core
{
    public class PowerGuiAssemblyLoader
    {
        private static readonly ILog Log = LogManager.GetLogger(typeof (PowerGuiAssemblyLoader));
        private static bool assembliesLoaded;

        private static string _installpath;
        public static string InstallPath
        {
            get
            {
                if (String.IsNullOrEmpty(_installpath))
                {
                    GetInstallPath();
                }
                return _installpath;
            }
        }

        private static bool GetInstallPath()
        {
            Log.Debug("Entering GetInstallPath()");
            _installpath = String.Empty;
            try
            {
                Type type = Type.GetTypeFromProgID("WindowsInstaller.Installer");
                dynamic msi = Activator.CreateInstance(type);

                Version invalidVersion = null;

                Log.Debug("Finding PowerGUI installing location...");
                foreach (var product in msi.RelatedProducts("{FCD94F6A-60AA-4E61-A27B-6344C59938AD}"))
                {
                    string productVersion = msi.ProductInfo(product, "VersionString");
                    Version version;
                    if (!Version.TryParse(productVersion, out version))
                    {
                        continue;
                    }

                    Log.InfoFormat("Located product version [{0}]", productVersion);

                    var supportedVersion = Version.Parse(Resources.SupportedVersion);

                    if (version >= supportedVersion)
                    {
                        Log.InfoFormat("Version matches supported version [{0}]. Locating installation path.",
                                       supportedVersion);
                        _installpath = msi.ProductInfo(product, "InstallLocation");
                        Log.InfoFormat("Installation path [{0}].", _installpath);
                    }
                    else
                    {
                        invalidVersion = version;
                    }
                }

                //We didn't find PG, lets look for PG pro.
                if (String.IsNullOrEmpty(_installpath))
                {
                    Log.Debug("Finding PowerGUI Pro installing location...");
                    foreach (var product in msi.RelatedProducts("{20EDF085-6376-4E33-BB8C-734FE6690634}"))
                    {
                        string productVersion = msi.ProductInfo(product, "VersionString");
                        Version version;
                        if (!Version.TryParse(productVersion, out version))
                        {
                            continue;
                        }

                        Log.InfoFormat("Located product version [{0}]", productVersion);

                        var supportedVersion = Version.Parse(Resources.SupportedVersion);

                        if (version >= supportedVersion)
                        {
                            Log.InfoFormat("Version matches supported version [{0}]. Locating installation path.",
                                           supportedVersion);
                            _installpath = msi.ProductInfo(product, "InstallLocation");
                            Log.InfoFormat("Installation path [{0}].", _installpath);
                        }
                        else
                        {
                            //Invalid version of PG pro installed
                            PowerGuiInstallationException.ThrowOnInvalidVersion(version);
                        }
                    }
                }

                //We have an invalid version of PowerGUI installed
                if (String.IsNullOrEmpty(_installpath) && invalidVersion != null)
                {
                    PowerGuiInstallationException.ThrowOnInvalidVersion(invalidVersion);
                }

                //PowerGUI is not installed at all
                if (String.IsNullOrEmpty(_installpath))
                {
                    PowerGuiInstallationException.ThrowOnNotInstalled();
                }
            }
            catch (Exception ex)
            {
                Log.Error("Error locating PowerGUI installation path.", ex);
                Trace.WriteLine(ex.Message);
                throw;
            }
            return true;
        }


        /// <summary>
        /// Loads the assemblies.
        /// </summary>
        /// <returns>The PowerGuiVsx.Core assembly.</returns>
        public static void LoadAssemblies()
        {
            if (assembliesLoaded)
            {
                return;
            }

            AppDomain currentDomain = AppDomain.CurrentDomain;
            currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve);

            Log.Debug("Entering LoadAssemblies().");           
            Assembly scriptEditor = Assembly.LoadFrom(Path.Combine(InstallPath, "ScriptEditor.exe"));
            
            var assemblies = scriptEditor.GetReferencedAssemblies();
            foreach (var assembly in assemblies)
            {
                bool loaded = false;
                try
                    {
                    Assembly.LoadFrom(Path.Combine(InstallPath, assembly.Name + ".dll"));
                    loaded = true;
                    }
                catch (FileNotFoundException)
                    {}
                if (!loaded)
                    Assembly.Load(assembly.FullName);
            }
            
            assembliesLoaded = true;
            
        }

        static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {

            if (String.IsNullOrEmpty(_installpath))
            {
                return null;
            }

            var assemblyName = args.Name.Split(',')[0];

            try
            {
                var path = Path.Combine(_installpath, assemblyName + ".dll");
                if (File.Exists(path))
                {
                    return Assembly.LoadFrom(path);        
                }
                path = Path.Combine(_installpath, assemblyName + ".exe");
                if (File.Exists(path))
                {
                    return Assembly.LoadFrom(path);
                }
                return null;
            }
            catch
            {
                return null;
            }
        }
    }
}
